package p2pmodule;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

import core.Util;

import events.TransferFinishedEvent;
import events.TransferFinishedListener;

/**
 * This class represents a single file transfer from the FileServer to the peer
 * on the other side of the socket.
 * 
 * @author ole
 */
public class ServerTransfer extends Transfer {
	// Class attributes.
	private Socket socket;
	private volatile boolean stop;
	private String shared_dir;
	
	/*
	 * Private methods.
	 */
	
	/**
	 * Method that checks that the filename passed exists inside the shared
	 * directory of Dust.
	 * 
	 * It also makes some jailing checks.
	 * 
	 * @param fname:
	 * 		The filename to be checked.
	 * @return:
	 * 		True if the filename exists and has passed the checks, false
	 * 		elsewhere.
	 */
	private boolean checkFilename(String fname) {
		File fd;
		
		// Check traverse path.
		if (Util.checkTraversePath(fname))
			return false;
		
		// Check if the file exists in the shared directory.
		fd = new File(shared_dir + fname);
		if (!fd.exists())
			return false;
		
		return true;
	}
	
	/*
	 * Public methods.
	 */

	/**
	 * Class constructor.
	 * 
	 * @param socket:
	 * 		The socket opened of the new connection that requested a transfer.
	 * @param shared_dir:
	 * 		The shared directory where the file requested must be searched.
	 */
	public ServerTransfer(Socket socket, String shared_dir) {
		super();
		this.socket = socket;
		this.shared_dir = Util.checkDirectoryPath(shared_dir);
		setName("ServerTransfer for " + socket.getInetAddress().toString());
	}
	
	/**
	 * Start handling the connection. We first read from the socket the
	 * filename and then sends it through the socket.
	 */
	@Override
	public void run() {
		TransferFinishedEvent event;
		InputStream is;
		OutputStream os;
		String fname;
		File fd;
		FileInputStream fis;
		byte buffer[];
		int len;
		
		try {
			is = socket.getInputStream();
			buffer = new byte[MAX_FNAME];
			
			// Get the filename.
			/** TODO: This expects the filename to be read just in a single
			 * read() call. Most of the times it will does it but this cannot
			 * ensures that it always does. This is a poor implementation.
			 * Improve is needed. **/
			is.read(buffer);
			fname = new String(buffer);
			
			// Check that the file requested exists.
			if (checkFilename(fname)) {
				fd = new File(shared_dir + fname);
				fis = new FileInputStream(fd);
				len = (int)fd.length();
				buffer = Util.recoverDatafromInputStream(fis, len);
				fis.close();
				
				os = socket.getOutputStream();
				os.write(buffer);
				os.close();
			}
			
			socket.close();
		
			// Create the event and call all the listeners with it.
			event = new TransferFinishedEvent(this);
			for (TransferFinishedListener listener: tf_listeners)
				listener.handleEvent(event);
		}
		// We can reach here either because of problems in the connection or
		// because someone has asked us to stop.
		catch (IOException e) {
			// Problems in the connection.
			if (!stop)
				e.printStackTrace();
		}
	}
	
	/**
	 * Implementation of the stopTranfer() method of superclass Transfer.
	 * 
	 * Finish the transfer.
	 */
	public void stopTransfer() {
		stop = true;
		try {
			socket.close();
		}
		catch (IOException e) {}
	}
}
